package lfp.engine.integration;

import lfp.engine.IForecastModel;
import lfp.engine.efop.ArmaxModel;
import lfp.engine.similarity.SimilarityModel;
import lfp.engine.arma.ArmaModel;
import lfp.engine.extrapolation.ExtrapolationModel;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: zhangsiyuan
 * Date: 2006-8-22
 * Time: 15:27:46
 */
public class IntegrationModel implements IForecastModel {
    private final static int testPointNum = 3;
    private List<IForecastModel> methodSet;

    public IntegrationModel() {
        methodSet = new ArrayList<IForecastModel>();
        methodSet.add(new ExtrapolationModel());
        methodSet.add(new ArmaModel());
        methodSet.add(new SimilarityModel());
        methodSet.add(new ArmaxModel());
    }

    private double [] getWeight(double[] series, int series_Period) {
        IForecastModel methodModel;
        int numOfMethodIntegrated = methodSet.size();
        double [] weight = new double[numOfMethodIntegrated];
        double [] temp_h = new double[numOfMethodIntegrated];
        double temp = 0;
        double [] sFore = new double[series.length - testPointNum];
        double [] sOrg = new double[testPointNum];
        double [][] resultSet = new double[numOfMethodIntegrated][sOrg.length];
        System.arraycopy(series, 0, sFore, 0, sFore.length);
        System.arraycopy(series, sFore.length, sOrg, 0, sOrg.length);
        int i = 0;
        for (Iterator it = methodSet.iterator(); it.hasNext();) {
            methodModel = (IForecastModel)it.next();
            resultSet[i] = methodModel.forecast(sFore, series_Period, testPointNum);
            i++;
        }
        for (int k = 0; k < numOfMethodIntegrated; k++) {
            for (int j = 0; j < testPointNum; j++) {
                temp_h[k] += Math.pow(resultSet[k][j] - sOrg[j], 2);
            }
            temp += 1 / temp_h[k];
        }
        for (int k = 0; k < numOfMethodIntegrated; k++) {
            weight[k] = 1 / (temp_h[k] * temp);
        }
        return weight;
    }

    public double[] forecast(double[] series, int series_Period, int forelength) {
        try {
            return forecastSerious(series, series_Period, forelength);
        } catch(Exception e) {
            return new ExtrapolationModel().forecast(series, series_Period, forelength);
        }
    }

    public double[] forecastSerious(double[] series, int series_Period, int forelength) {
        IForecastModel methodModel;
        int numOfMethodIntegrated = methodSet.size();
        double [][] resultSet = new double[numOfMethodIntegrated][forelength];
        double [] result = new double[forelength];
        int i = 0;
        for (Iterator it = methodSet.iterator(); it.hasNext();) {
            methodModel = (IForecastModel)it.next();
            resultSet[i] = methodModel.forecast(series, series_Period, forelength);
            i++;
        }
        double[] seriesForWeight = new double[series.length - series_Period + testPointNum];
        System.arraycopy(series,series_Period - testPointNum, seriesForWeight, 0, seriesForWeight.length);
        double [] weight = getWeight(seriesForWeight,series_Period);
        for(int j = 0; j< forelength;j++){
            for(int k = 0; k < numOfMethodIntegrated;k++){
                result[j] += weight[k]*resultSet[k][j];
            }
        }
        return result;
    }
}
